# coding=utf-8
# Created:     20/07/2012
# Copyright:   http://www.cnblogs.com/txw1958/
'''
A Chinese Calendar Library in Python
'''

import base64
import datetime
import re
import time

__version__ = "$Rev: 123 $"
__all__ = ['LunarDate']

solar_year = 1900
solar_month = 1
solar_day = 31
solar_weekday = 0

lunar_year = 0
lunar_month = 0
lunar_day = 0
lunar_isLeapMonth = False


class LunarDate(object):
    _startDate = datetime.date(1900, 1, 31)

    def __init__(self, year, month, day, isLeapMonth=False):
        global lunar_year
        global lunar_month
        global lunar_day
        global lunar_isLeapMonth

        lunar_year = int(year)
        lunar_month = int(month)
        lunar_day = int(day)
        lunar_isLeapMonth = bool(isLeapMonth)

        self.year = year
        self.month = month
        self.day = day
        self.isLeapMonth = bool(isLeapMonth)

    def __str__(self):
        return 'LunarDate(%d, %d, %d, %d)' % (self.year, self.month, self.day, self.isLeapMonth)

    __repr__ = __str__

    @staticmethod
    def fromSolarDate(year, month, day):
        solarDate = datetime.date(year, month, day)
        offset = (solarDate - LunarDate._startDate).days
        return LunarDate._fromOffset(offset)

    def toSolarDate(self):
        def _calcDays(yearInfo, month, day, isLeapMonth):
            isLeapMonth = int(isLeapMonth)
            res = 0
            ok = False
            for _month, _days, _isLeapMonth in self._enumMonth(yearInfo):
                if (_month, _isLeapMonth) == (month, isLeapMonth):
                    if 1 <= day <= _days:
                        res += day - 1
                        return res
                    else:
                        raise ValueError("day out of range")
                res += _days

            raise ValueError("month out of range")

        offset = 0
        if self.year < 1900 or self.year >= 2050:
            raise ValueError('year out of range [1900, 2050)')
        yearIdx = self.year - 1900
        for i in range(yearIdx):
            offset += yearDays[i]

        offset += _calcDays(yearInfos[yearIdx], self.month, self.day, self.isLeapMonth)
        return self._startDate + datetime.timedelta(days=offset)

    def __sub__(self, other):
        if isinstance(other, LunarDate):
            return self.toSolarDate() - other.toSolarDate()
        elif isinstance(other, datetime.date):
            return self.toSolarDate() - other
        elif isinstance(other, datetime.timedelta):
            res = self.toSolarDate() - other
            return LunarDate.fromSolarDate(res.year, res.month, res.day)
        raise TypeError

    def __rsub__(self, other):
        if isinstance(other, datetime.date):
            return other - self.toSolarDate()

    def __add__(self, other):
        if isinstance(other, datetime.timedelta):
            res = self.toSolarDate() + other
            return LunarDate.fromSolarDate(res.year, res.month, res.day)
        raise TypeError

    def __radd__(self, other):
        return self + other

    def __lt__(self, other):
        return self - other < datetime.timedelta(0)

    def __le__(self, other):
        return self - other <= datetime.timedelta(0)

    @classmethod
    def today(cls):
        res = datetime.date.today()
        return cls.fromSolarDate(res.year, res.month, res.day)

    @staticmethod
    def _enumMonth(yearInfo):
        months = [(i, 0) for i in range(1, 13)]
        leapMonth = yearInfo % 16
        if leapMonth == 0:
            pass
        elif leapMonth <= 12:
            months.insert(leapMonth, (leapMonth, 1))
        else:
            raise ValueError("yearInfo %r mod 16 should in [0, 12]" % yearInfo)

        for month, isLeapMonth in months:
            if isLeapMonth:
                days = (yearInfo >> 16) % 2 + 29
            else:
                days = (yearInfo >> (16 - month)) % 2 + 29
            yield month, days, isLeapMonth

    @classmethod
    def _fromOffset(cls, offset):
        def _calcMonthDay(yearInfo, offset):
            for month, days, isLeapMonth in cls._enumMonth(yearInfo):
                if offset < days:
                    break
                offset -= days
            return (month, offset + 1, isLeapMonth)

        offset = int(offset)

        for idx, yearDay in enumerate(Info.yearDays()):
            if offset < yearDay:
                break
            offset -= yearDay
        year = 1900 + idx

        yearInfo = Info.year_info[idx]
        month, day, isLeapMonth = _calcMonthDay(yearInfo, offset)
        return LunarDate(year, month, day, isLeapMonth)


class ChineseWord:

    @staticmethod
    def weekday_str(tm):
        a = '星期日 星期一 星期二 星期三 星期四 星期五 星期六'.split()
        return a[tm]

    @staticmethod
    def solarTerm(year, month, day):
        a = '小寒 大寒 立春 雨水 惊蛰 春分\
             清明 谷雨 立夏 小满 芒种 夏至\
             小暑 大暑 立秋 处暑 白露 秋分\
             寒露 霜降 立冬 小雪 大雪 冬至'.split()
        return

    @staticmethod
    def day_lunar(ld):
        a = '初一 初二 初三 初四 初五 初六 初七 初八 初九 初十\
             十一 十二 十三 十四 十五 十六 十七 十八 十九 廿十\
             廿一 廿二 廿三 廿四 廿五 廿六 廿七 廿八 廿九 三十'.split()
        return a[ld - 1]

    @staticmethod
    def month_lunar(le, lm):
        a = '正月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月'.split()
        if le:
            return "闰" + a[lm - 1]
        else:
            return a[lm - 1]

    @staticmethod
    def year_lunar(ly):
        y = ly
        tg = '甲 乙 丙 丁 戊 己 庚 辛 壬 癸'.split()
        dz = '子 丑 寅 卯 辰 巳 午 未 申 酉 戌 亥'.split()
        sx = '鼠 牛 虎 兔 龙 蛇 马 羊 猴 鸡 狗 猪'.split()
        return tg[(y - 4) % 10] + dz[(y - 4) % 12] + '[' + sx[(y - 4) % 12] + ']' + '年'


class Festival():
    # 国历节日 *表示放假日
    @staticmethod
    def solar_Fstv(solar_month, solar_day):
        sFtv = [
            "0101#元旦节#",
            "0202#世界湿地日#",
            "0210#国际气象节#",
            "0214#情人节#",
            "0301#国际海豹日#",
            "0303#全国爱耳日#",
            "0305#学雷锋纪念日#",
            "0308#妇女节#",
            "0312#植树节# #孙中山逝世纪念日#",
            "0314#国际警察日#",
            "0315#消费者权益日#",
            "0317#中国国医节# #国际航海日#",
            "0321#世界森林日# #消除种族歧视国际日# #世界儿歌日#",
            "0322#世界水日#",
            "0323#世界气象日#",
            "0324#世界防治结核病日#",
            "0325#全国中小学生安全教育日#",
            "0330#巴勒斯坦国土日#",
            "0401#愚人节# #全国爱国卫生运动月(四月)# #税收宣传月(四月)#",
            "0407#世界卫生日#",
            "0422#世界地球日#",
            "0423#世界图书和版权日#",
            "0424#亚非新闻工作者日#",
            "0501#劳动节#",
            "0504#青年节#",
            "0505#碘缺乏病防治日#",
            "0508#世界红十字日#",
            "0512#国际护士节#",
            "0515#国际家庭日#",
            "0517#国际电信日#",
            "0518#国际博物馆日#",
            "0520#全国学生营养日#",
            "0523#国际牛奶日#",
            "0531#世界无烟日#",
            "0601#国际儿童节#",
            "0605#世界环境保护日#",
            "0606#全国爱眼日#",
            "0617#防治荒漠化和干旱日#",
            "0623#国际奥林匹克日#",
            "0625#全国土地日#",
            "0626#国际禁毒日#",
            "0701#中国共·产党诞辰# #香港回归纪念日# #世界建筑日#",
            "0702#国际体育记者日#",
            "0707#抗日战争纪念日#",
            "0711#世界人口日#",
            "0730#非洲妇女日#",
            "0801#建军节#",
            "0808#中国男子节(爸爸节)#",
            "0815#抗日战争胜利纪念#",
            "0908#国际扫盲日# #国际新闻工作者日#",
            "0909#毛·泽东逝世纪念#",
            "0910#中国教师节#",
            "0914#世界清洁地球日#",
            "0916#国际臭氧层保护日#",
            "0918#九·一八事变纪念日#",
            "0920#国际爱牙日#",
            "0927#世界旅游日#",
            "0928#孔子诞辰#",
            "1001#国庆节# #世界音乐日# #国际老人节#",
            "1002#国庆节假日# #国际和平与民主自由斗争日#",
            "1003#国庆节假日#",
            "1004#世界动物日#",
            "1006#老人节#",
            "1008#全国高血压日# #世界视觉日#",
            "1009#世界邮政日# #万国邮联日#",
            "1010#辛亥革命纪念日# #世界精神卫生日#",
            "1013#世界保健日# #国际教师节#",
            "1014#世界标准日#",
            "1015#国际盲人节(白手杖节)#",
            "1016#世界粮食日#",
            "1017#世界消除贫困日#",
            "1022#世界传统医药日#",
            "1024#联合国日#",
            "1031#世界勤俭日#",
            "1107#十月社会主义革命纪念日#",
            "1108#中国记者日#",
            "1109#全国消防安全宣传教育日#",
            "1110#世界青年节#",
            "1111#国际科学与和平周(本日所属的一周)#",
            "1112#孙中山诞辰纪念日#",
            "1114#世界糖尿病日#",
            "1116#国际宽容日#",
            "1117#国际大学生节# #世界学生节#",
            "1120#彝族年#",
            "1121#彝族年# #世界问候日# #世界电视日#",
            "1122#彝族年#",
            "1129#国际声援巴勒斯坦人民国际日#",
            "1201#世界艾滋病日#",
            "1203#世界残疾人日#",
            "1205#国际经济和社会发展志愿人员日#",
            "1208#国际儿童电视日#",
            "1209#世界足球日#",
            "1210#世界人权日#",
            "1212#西安事变纪念日#",
            "1213#南京大屠杀(1937年)纪念日#",
            "1220#澳门回归纪念#",
            "1221#国际篮球日#",
            "1224#平安夜#",
            "1225#圣诞节#",
            "1226#毛·泽东诞辰纪念日#"
        ]
        solar_month_str = str(solar_month) if solar_month > 9 else "0" + str(solar_month)
        solar_day_str = str(solar_day) if solar_day > 9 else "0" + str(solar_day)
        pattern = "(" + solar_month_str + solar_day_str + ")([\w+?\#?\(?\)?\d+\s?·?]*)"
        for solar_fstv_item in sFtv:
            result = re.search(pattern, solar_fstv_item)
            if result is not None:
                return result.group(2)

    @staticmethod
    def lunar_Fstv(lunar_month, lunar_day):
        # 农历节日 *表示放假日
        # 每年单独来算
        lFtv = [
            "0101#春节#",
            "0115#元宵节#",
            "0202#春龙节",
            # "0314#清明节#", #每年不一样，此为2012年，事实上为公历节日
            "0505#端午节#",
            "0707#七夕情人节#",
            "0715#中元节#",
            "0815#中秋节#",
            "0909#重阳节#",
            "1208#腊八节#",
            "1223#小年#",
            "1229#除夕#"  # 每年不一样，此为2011年
        ]
        lunar_month_str = str(lunar_month) if lunar_month > 9 else "0" + str(lunar_month)
        lunar_day_str = str(lunar_day) if lunar_day > 9 else "0" + str(lunar_day)
        pattern = "(" + lunar_month_str + lunar_day_str + ")([\w+?\#?\s?]*)"
        for lunar_fstv_item in lFtv:
            result = re.search(pattern, lunar_fstv_item)
            if result is not None:
                return result.group(2)

    # 国历节日 *表示放假日
    @staticmethod
    def weekday_Fstv(solar_month, solar_day, solar_weekday):
        # 某月的第几个星期几
        wFtv = [
            "0150#世界防治麻风病日#",  # 一月的最后一个星期日（月倒数第一个星期日）
            "0520#国际母亲节#",
            "0530#全国助残日#",
            "0630#父亲节#",
            "0730#被奴役国家周#",
            "0932#国际和平日#",
            "0940#国际聋人节# #世界儿童日#",
            "0950#世界海事日#",
            "1011#国际住房日#",
            "1013#国际减轻自然灾害日(减灾日)#",
            "1144#感恩节#"]

        # 7，14等应该属于1, 2周，能整除的那天实际属于上一周，做个偏移
        offset = -1 if solar_day % 7 == 0 else 0
        # 计算当前日属于第几周，得出来从0开始计周，再向后偏移1
        weekday_ordinal = solar_day // 7 + offset + 1

        solar_month_str = str(solar_month) if solar_month > 9 else "0" + str(solar_month)
        solar_weekday_str = str(weekday_ordinal) + str(solar_weekday)

        pattern = "(" + solar_month_str + solar_weekday_str + ")(\\w+?\\#?\s?]*)"
        for weekday_fstv_item in wFtv:
            result = re.search(pattern, weekday_fstv_item)
            if result is not None:
                return result.group(2)

        # 如何计算某些最后一个星期几的情况，..........

    # 24节气
    @staticmethod
    def solar_Term(solar_month, solar_day):
        # 每年数据不一样，此为2012年内的数据
        stFtv = [
            "0106#小寒#",
            "0120#大寒#",
            "0204#立春#",
            "0219#雨水#",
            "0305#惊蛰#",
            "0320#春分#",
            "0404#清明#",
            "0420#谷雨#",
            "0505#立夏#",
            "0521#小满#",
            "0605#芒种#",
            "0621#夏至#",
            "0707#小暑#",
            "0722#大暑#",
            "0807#立秋#",
            "0823#处暑#",
            "0907#白露#",
            "0922#秋分#",
            "1008#寒露#",
            "1023#霜降#",
            "1107#立冬#",
            "1122#小雪#",
            "1206#大雪#",
            "1221#冬至#",
        ]
        solar_month_str = str(solar_month) if solar_month > 9 else "0" + str(solar_month)
        solar_day_str = str(solar_day) if solar_day > 9 else "0" + str(solar_day)
        pattern = "(" + solar_month_str + solar_day_str + ")([\w+?\#?]*)"
        for solarTerm_fstv_item in stFtv:
            result = re.search(pattern, solarTerm_fstv_item)
            if result is not None:
                return result.group(2)


class Info:
    year_info = [
        #    /* encoding:
        #               b bbbbbbbbbbbb bbbb
        #       bit#    1 111111000000 0000
        #               6 543210987654 3210
        #               . ............ ....
        #       month#    000000000111
        #               M 123456789012   L
        #
        #    b_j = 1 for long month, b_j = 0 for short month
        #    L is the leap month of the year if 1<=L<=12; NO leap month if L = 0.
        #    The leap month (if exists) is long one iff M = 1.
        #    */
        0x04bd8,  # /* 1900 */
        0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950,  # /* 1905 */
        0x16554, 0x056a0, 0x09ad0, 0x055d2, 0x04ae0,  # /* 1910 */
        0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540,  # /* 1915 */
        0x0d6a0, 0x0ada2, 0x095b0, 0x14977, 0x04970,  # /* 1920 */
        0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54,  # /* 1925 */
        0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566,  # /* 1930 */
        0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60,  # /* 1935 */
        0x186e3, 0x092e0, 0x1c8d7, 0x0c950, 0x0d4a0,  # /* 1940 */
        0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0,  # /* 1945 */
        0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0,  # /* 1950 */
        0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573,  # /* 1955 */
        0x052d0, 0x0a9a8, 0x0e950, 0x06aa0, 0x0aea6,  # /* 1960 */
        0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260,  # /* 1965 */
        0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0,  # /* 1970 */
        0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250,  # /* 1975 */
        0x0d558, 0x0b540, 0x0b5a0, 0x195a6, 0x095b0,  # /* 1980 */
        0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50,  # /* 1985 */
        0x06d40, 0x0af46, 0x0ab60, 0x09570, 0x04af5,  # /* 1990 */
        0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58,  # /* 1995 */
        0x05ac0, 0x0ab60, 0x096d5, 0x092e0, 0x0c960,  # /* 2000 */
        0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0,  # /* 2005 */
        0x0abb7, 0x025d0, 0x092d0, 0x0cab5, 0x0a950,  # /* 2010 */
        0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0,  # /* 2015 */
        0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954,  # /* 2020 */
        0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6,  # /* 2025 */
        0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, 0x05aa0,  # /* 2030 */
        0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0,  # /* 2035 */
        0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0,  # /* 2040 */
        0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0,  # /* 2045 */
        0x0aa50, 0x1b255, 0x06d20, 0x0ada0  # /* 2049 */
    ]

    @staticmethod
    def yearInfo2yearDay(yearInfo):
        yearInfo = int(yearInfo)

        res = 29 * 12

        leap = False
        if yearInfo % 16 != 0:
            leap = True
            res += 29

        yearInfo //= 16

        for i in range(12 + leap):
            if yearInfo % 2 == 1:
                res += 1
            yearInfo //= 2
        return res

    @staticmethod
    def yearDays():
        yearDays = [Info.yearInfo2yearDay(x) for x in Info.year_info]
        return yearDays

    @staticmethod
    def day2LunarDate(offset):
        offset = int(offset)
        res = LunarDate()
        for idx, yearDay in enumerate(Info.yearDays()):
            if offset < yearDay:
                break
            offset -= yearDay
        res.year = 1900 + idx


class SolarDate:

    def __init__(self):
        global solar_year
        global solar_month
        global solar_day
        global solar_weekday

        solar_year = int(time.strftime("%Y", time.localtime()))
        solar_month = int(time.strftime("%m", time.localtime()))
        solar_day = int(time.strftime("%d", time.localtime()))
        solar_weekday = int(time.strftime("%w", time.localtime()))

        self.year = solar_year
        self.month = solar_month
        self.day = solar_day
        self.weekday = solar_weekday

    def __str__(self):
        return 'LunarDate(%d, %d, %d, %d)' % (self.year, self.month, self.day, self.isLeapMonth)


def getCalendar_from_solar_date(year=2020, month=9, day=1):
    global solar_year
    global solar_month
    global solar_day

    solar_year = year
    solar_month = month
    solar_day = day

    # solar = SolarDate()
    LunarDate.fromSolarDate(solar_year, solar_month, solar_day)

    festival = ""
    if Festival.solar_Term(solar_month, solar_day):
        festival = festival + " 今日节气：" + Festival.solar_Term(solar_month, solar_day)
    if Festival.solar_Fstv(solar_month, solar_day):
        festival = festival + " 公历节日：" + Festival.solar_Fstv(solar_month, solar_day)
    if Festival.weekday_Fstv(solar_month, solar_day, solar_weekday):
        if festival.find("公历节日") == -1:
            festival = festival + " 公历节日：" + Festival.weekday_Fstv(solar_month, solar_day, solar_weekday)
        else:
            festival = festival + " " + Festival.weekday_Fstv(solar_month, solar_day, solar_weekday)
    if Festival.lunar_Fstv(lunar_month, lunar_day):
        festival = festival + " 农历节日：" + Festival.lunar_Fstv(lunar_month, lunar_day)

    twitter = \
        "今天是" + str(solar_year) + "年" + str(solar_month) + "月" + str(solar_day) + "日" + " " \
        + ChineseWord.weekday_str(solar_weekday) + " 农历" + ChineseWord.year_lunar(lunar_year) \
        + ChineseWord.month_lunar(lunar_isLeapMonth, lunar_month) \
        + ChineseWord.day_lunar(lunar_day) + festival
    return twitter


def getCalendar_all_day(year=2012):
    # solar = SolarDate()
    global solar_year
    global solar_month
    global solar_day
    global solar_weekday

    solar_year = year
    solar_month = 1
    weekday_offset = datetime.datetime(year, 1, 1).weekday()  # 1月1号星期几?
    # solar_weekday = weekday_offset + 1

    index_day = 0
    for solar_month in range(1, 13):
        if solar_month in [1, 3, 5, 7, 8, 10, 12]:
            solar_day_max = 31
        elif solar_month in [4, 6, 9, 11]:
            solar_day_max = 30
        elif solar_month == 2:
            if ((solar_year % 4 == 0) and (solar_year % 100 != 0)) or (solar_year % 400 == 0):
                solar_day_max = 29
            else:
                solar_day_max = 28
        else:
            pass

        for solar_day in range(1, solar_day_max + 1):
            index_day += 1
            solar_weekday = (index_day + weekday_offset) % 7
            solar_weekday = 0 if solar_weekday == 7 else solar_weekday
            solar_weekday = 6 if solar_weekday == -1 else solar_weekday

            LunarDate.fromSolarDate(solar_year, solar_month, solar_day)
            festival = ""

            if Festival.solar_Term(solar_month, solar_day):
                festival = festival + " 节气：" + Festival.solar_Term(solar_month, solar_day)
            if Festival.solar_Fstv(solar_month, solar_day):
                festival = festival + " 节日：" + Festival.solar_Fstv(solar_month, solar_day)
            if Festival.weekday_Fstv(solar_month, solar_day, solar_weekday):
                if festival.find("节日") == -1:
                    festival = festival + " 节日：" + Festival.weekday_Fstv(solar_month, solar_day, solar_weekday)
                else:
                    festival = festival + " " + Festival.weekday_Fstv(solar_month, solar_day, solar_weekday)
            if Festival.lunar_Fstv(lunar_month, lunar_day):
                if festival.find("节日") == -1:
                    festival = festival + " 节日：" + Festival.lunar_Fstv(lunar_month, lunar_day)
                else:
                    festival = festival + " " + Festival.lunar_Fstv(lunar_month, lunar_day)

            index_yy = str(solar_year)
            if int(solar_month) < 10:
                index_mm = "0" + str(solar_month)
            else:
                index_mm = str(solar_month)
            if int(solar_day) < 10:
                index_dd = "0" + str(solar_day)
            else:
                index_dd = str(solar_day)

            index_yyddmm = index_yy + index_mm + index_dd

            twitter = ("message(" + str(index_yyddmm) + ') = "') + \
                      str(solar_year) + "年" + str(solar_month) + "月" + str(solar_day) + "日" + " " \
                      + ChineseWord.weekday_str(solar_weekday) + " 农历" + ChineseWord.year_lunar(lunar_year) \
                      + ChineseWord.month_lunar(lunar_isLeapMonth, lunar_month) \
                      + ChineseWord.day_lunar(lunar_day) + festival + '"'
            print(twitter)

    return twitter


def main():
    """main function"""
    print(base64.b64decode(b'Q29weXJpZ2h0IChjKSAyMDEyIERvdWN1YmUgSW5jLiBBbGwgcmlnaHRzIHJlc2VydmVkLg==').decode())
    # getCalendar_all_day()
    print(getCalendar_from_solar_date())


if __name__ == '__main__':
    main()
